package nr.nodehost;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

import lombok.Data;
import lombok.Getter;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import nr.nodehost.model.Component;

@Data
public class ComponentExecutor {
	
	private JSONObject param;
	private SchemaExecutor schemaExecutor;
	private JSONObject executeContext;
	private String id;
	@Getter
	private String program;
	@Getter
	private int nextFrameNo;
	private Dispatcher dispatcher;
	private int loopNumber;
	//private boolean needTerminate=false;
	//private boolean needPause=false;
	private HashMap<String,Queue<DataFrame>> inputQueues=new HashMap<String,Queue<DataFrame>>();
	private ArrayList<String> commandArgs=new ArrayList<String>();
	@Getter
	private int status=SchemaExecutor.IDLE;

	private Process ComponentProcess;
	private String ComponentDocker;
	
	public boolean init(String id,JSONObject executeContext,SchemaExecutor schemaExecutor,JSONObject param,Dispatcher dispatcher) {
		this.id=id;
		this.schemaExecutor=schemaExecutor;
		this.param=param;
		this.executeContext=executeContext;
		this.dispatcher=dispatcher;
		this.loopNumber=schemaExecutor.getLoopNumber();
		//if(param.getString("uuid").equals("52e1eafc-bcb8-41cb-82e8-7ed8433b1297"))
		//	System.out.println("debug");
		program=String.format("%s/%s/%s", schemaExecutor.getSchemaRunDir(),param.getString("uuid"),param.getString("uuid"));
		nextFrameNo=1;
		JSONObject functionParam=param.getJSONObject("functionParam");
		int inputNumber=functionParam.getInt("inputNumber");
		JSONArray inputParams=functionParam.getJSONArray("inputParams");
		for(int i=0;i<inputNumber;i++) {
			JSONObject inputParam=inputParams.getJSONObject(i);
			String sourceType="other";
			String portName=inputParam.getString("name");
			Queue<DataFrame> inputQueue=new LinkedList<DataFrame>();
			inputQueues.put(portName, inputQueue);
		}
		return true;
	}

	public boolean isNeedTerminate() {
		return schemaExecutor.isNeedTerminate();
	}

	public boolean isNeedPause() {
		return schemaExecutor.isNeedPause();
	}

	public void setStatus(int status) {
		this.status=status;
	}
	public boolean finished() {
		return loopNumber > 0 && nextFrameNo > loopNumber;
	}
	
	public int getExecuteNumber() {
		return nextFrameNo;
	}
	
	public String getComponentId() {
		return param.getString("uuid");
	}
	
	public String getComponentName() {
		return param.getString("id");
	}
	public String getComponentRunLibPath() {
		return executeContext.getString("matlabLibDir")+":"+this.executeContext.getString("componentsDir")+"/"+id;
	}
	
	public void dataArrived(DataFrame frame) {
		System.out.println("component "+param.getString("uuid")+" data arrived");
		/*
		if(param.getString("uuid").equals("5b5e1936-847a-4c27-ac18-6b7df0045110")) {
			System.out.println("debug");
		}
		if(param.getString("uuid").equals("52e1eafc-bcb8-41cb-82e8-7ed8433b1297")) {
			System.out.println("debug2");
			System.out.println(frame.getTargetComponentPort().length());
			for(String keys:inputQueues.keySet())
				System.out.println(keys+":"+keys.length());
		}
		*/
		String targetComponentPort=frame.getTargetComponentPort();
		Queue<DataFrame> inputQueue=inputQueues.get(targetComponentPort);
		if(inputQueue!=null) {
			String inputFilename=String.format("%s/%s_%06d.dat", schemaExecutor.getComponentInputDir(param.getString("uuid")),frame.getTargetComponentPort(),frame.getFrameNo());
			frame.saveToFile(inputFilename);
			inputQueue.add(frame);
		}
	}
	
	public boolean canRunOnce() {
		/*
		if(param.getString("uuid").equals("5b5e1936-847a-4c27-ac18-6b7df0045110")) {
			System.out.println("debug");
		}
		*/
		boolean result=true;
		for(Queue<DataFrame> inputQueue:inputQueues.values()) {
			DataFrame frame=inputQueue.peek();
			if(frame==null||frame.getFrameNo()!=nextFrameNo||nextFrameNo>schemaExecutor.getExecutedNumber()+2) {
				result=false;
				break;
			}
		}
		return result;
	}
	
	public void finishRun() {
		if(param.getInt("type")!=Component.NET_OUTPUT_COMPONENT) {
			dispatchCurrentOutput();
		}
		removeCurrentDataFrame();
		removeDataFiles();
		nextFrameNo++;
	}
	
	public String[] getCommandArgs() {
		return commandArgs.toArray(new String[0]);
	}

	public String getProgramDir() {
        return schemaExecutor.getComponentDir(param.getString("uuid"));
	}
	
	public void removeCurrentDataFrame() {
		for(Queue<DataFrame> inputQueue:inputQueues.values()) {
			DataFrame frame=inputQueue.poll();
			//frame.freeData();
			frame=null;
			System.gc();
		}
		
	}

	public void removeComponentMap() {
		dispatcher.getComponentExecutorMap().remove(this.getComponentId());
	}
	
	public void dispatchCurrentOutput() {
		
		if(param.getString("id").equalsIgnoreCase("sda"))
			System.out.println("sda component dispatch output");
		else if(param.getString("id").equalsIgnoreCase("svg"))
			System.out.println("svg component dispatch output");
		
		JSONObject functionParam=param.getJSONObject("functionParam");
		int outputNumber=functionParam.getInt("outputNumber");
		JSONArray outputParams=functionParam.getJSONArray("outputParams");
		for(int i=0;i<outputNumber;i++) {
			JSONObject outputParam=outputParams.getJSONObject(i);
			String outputFilename=String.format("%s/%s_%06d.dat", schemaExecutor.getComponentOutputDir(param.getString("uuid")),outputParam.getString("name"),nextFrameNo);
			if(new File(outputFilename).exists()) {
				List<JSONObject> targetComponents=schemaExecutor.getPortTargetComponents(param.getString("uuid"), outputParam.getString("name"));
				for(int j=0;j<targetComponents.size();j++) {
					int frameNo=nextFrameNo;
					//targetComponent.getString("id").equals(param.getString("uuid"))?nextFrameNo+1:nextFrameNo;
					JSONObject targetComponent=targetComponents.get(j);
					String targetPortName=schemaExecutor.getTargetPortName(param.getString("uuid"),outputParam.getString("name"),targetComponent.getString("id"));
					JSONObject componentParam=targetComponent.getJSONObject("componentParam");
					JSONObject targetFunctionParam=componentParam.getJSONObject("functionParam");
					JSONObject resourceParam=componentParam.getJSONObject("resourceParam");
					//int inputNumber=Integer.parseInt(functionParam.getString("inputNumber"));
					JSONArray targetInputParams=targetFunctionParam.getJSONArray("inputParams");
					
					for(int k=0;k<targetInputParams.size();k++) {
						JSONObject targetInputParam=targetInputParams.getJSONObject(k);
						if(targetInputParam.getString("name").equals(targetPortName)&&targetInputParam.has("useInitDataFile")&&targetInputParam.getBoolean("useInitDataFile")) {
							frameNo=nextFrameNo+1;
						}
					}
					if(resourceParam.optInt("node")==executeContext.getInt("nodeId")) {
						//target component in the same node
						try{
							DataFrame outFrame=new DataFrame(
						
								frameNo,//targetComponent.getString("id").equals(param.getString("uuid"))?nextFrameNo+1:nextFrameNo,
								schemaExecutor.getSchemaId(),
								param.getString("uuid"),
								outputParam.getString("name"),
								targetComponent.getString("id"),
								targetPortName,
								outputFilename);
							dispatcher.dataArrive(outFrame);
						}
						catch(Exception e) {
							e.printStackTrace();
						}
					}
					else {
						//target component in the different node
						JSONObject message=new JSONObject();
						message.put("messageType", "dataMessage");
						JSONObject messageContent=new JSONObject();
						messageContent.put("frameNo", frameNo);
						messageContent.put("schemaId", schemaExecutor.getSchemaId());
						messageContent.put("sourceComponentId", param.getString("uuid"));
						messageContent.put("sourceComponentPort", outputParam.getString("name"));
						messageContent.put("targetComponentId", targetComponent.getString("id"));
						messageContent.put("targetComponentPort", targetPortName);
						messageContent.put("dataFilename", outputFilename);
						message.put("messageContent", messageContent);
//						dispatcher.sendMessageToNode(resourceParam.optInt("node"),message);
					}
				}
			}
			else {
				System.out.println("outputfile "+outputFilename+" not exists");
			}
			
		}
		
	}
	
	public void removeDataFiles() {
		JSONObject resourceParam=param.getJSONObject("resourceParam");
		if(resourceParam.has("saveMode")){
			int saveMode=resourceParam.getInt("saveMode");
			if(saveMode>0) {
				int startFrame=1;
				int endFrame=100000;
				try {
					startFrame=resourceParam.getInt("startFrame");
					endFrame=resourceParam.getInt("endFrame");
				}
				catch(Exception e) {
					e.printStackTrace();
				}
				if(saveMode==1||(saveMode==2&&(nextFrameNo<startFrame||nextFrameNo>endFrame))) {
					JSONObject functionParam=param.getJSONObject("functionParam");
					int outputNumber=functionParam.getInt("outputNumber");
					JSONArray outputParams=functionParam.getJSONArray("outputParams");
					for(int i=0;i<outputNumber;i++) {
						JSONObject outputParam=outputParams.getJSONObject(i);
						String outputFilename=String.format("%s/%s_%06d.dat", schemaExecutor.getComponentOutputDir(param.getString("uuid")),outputParam.getString("name"),nextFrameNo);
						new File(outputFilename).delete();
					}
					int inputNumber=functionParam.getInt("inputNumber");
					JSONArray inputParams=functionParam.getJSONArray("inputParams");
					for(int i=0;i<inputNumber;i++) {
						JSONObject inputParam=inputParams.getJSONObject(i);
						String outputFilename=String.format("%s/%s_%06d.dat", schemaExecutor.getComponentInputDir(param.getString("uuid")),inputParam.getString("name"),nextFrameNo);
						new File(outputFilename).delete();
					}
				}
			}
		}
	}
}
